﻿using System.Collections.Generic;
using System.IO;
using System.Reflection;
using System.Windows;

namespace Gamelight.Data
{
	internal static class AssemblyStreamManager
	{
		private static string defaultAssembly;
		
		internal enum MediaType
		{
			Other = 0,
			Sound = 1,
			Image = 2
		}

		private static Dictionary<string, MediaType> fileExtensions = new Dictionary<string, MediaType>()
		{
			{ "jpg", MediaType.Image },
			{ "jpeg", MediaType.Image },
			{ "png", MediaType.Image },
			{ "gif", MediaType.Image },
			{ "bmp", MediaType.Image },
			{ "wmv", MediaType.Sound },
			{ "mp3", MediaType.Sound },
		};

		static AssemblyStreamManager()
		{
			Assembly rootAssembly = Application.Current.GetType().Assembly;
			AssemblyStreamManager.RegisterAssembly(rootAssembly);
			AssemblyStreamManager.defaultAssembly = AssemblyStreamManager.GetAssemblyShortName(rootAssembly);
		}

		internal static void SetDefaultAssembly(string assemblyName)
		{
			AssemblyStreamManager.defaultAssembly = assemblyName;
		}

		internal static string GetAssemblyShortName(System.Reflection.Assembly assembly)
		{
			return assembly.FullName.Split(',')[0];
		}

		internal static string CanonicalizeEmbeddedResourceName(string assembly, string pathInAssembly)
		{
			assembly = assembly ?? defaultAssembly;
			if (pathInAssembly[0] != '.' && pathInAssembly[0] != '/' && pathInAssembly[0] != '\\')
			{
				pathInAssembly = "." + pathInAssembly;
			}

			return CanonicalizeEmbeddedResourceName(assembly + pathInAssembly);
		}

		internal static string CanonicalizeEmbeddedResourceName(string rawPath)
		{
			return rawPath.Replace('/', '.');
		}

		private static Dictionary<string, Assembly> assemblyNames = new Dictionary<string, Assembly>();

		internal static Stream GetFreshStream(string assemblyName, string path)
		{
			assemblyName = assemblyName ?? defaultAssembly;
			string resourceName = CanonicalizeEmbeddedResourceName(assemblyName, path);
			Assembly assembly;

			if (!assemblyNames.TryGetValue(assemblyName, out assembly))
			{
				throw new AssemblyNotFoundException(assemblyName);
			}

			Stream stream = assembly.GetManifestResourceStream(resourceName);

			if (stream == null)
			{
				throw new ResourceNotFoundException(assemblyName, path);
			}

			return stream;
		}

		internal static Dictionary<string, Stream> GetAllStreams(string assemblyName, MediaType mediaType)
		{
			Assembly assembly;
			if (!assemblyNames.TryGetValue(assemblyName, out assembly))
			{
				throw new AssemblyNotFoundException(assemblyName);
			}

			Dictionary<string, Stream> streams = new Dictionary<string, Stream>();

			foreach (string resource in assembly.GetManifestResourceNames())
			{
				string[] parts = resource.Split('.');
				string fileExtension = parts[parts.Length - 1];
				MediaType type = MediaType.Other;
				fileExtensions.TryGetValue(fileExtension.ToLower(), out type);
				if (mediaType == type)
				{
					streams.Add(resource, assembly.GetManifestResourceStream(resource));
				}
			}

			return streams;
		}

		internal static void RegisterAssembly(Assembly assembly)
		{
			string assemblyShortName = GetAssemblyShortName(assembly);
			assemblyNames[assemblyShortName] = assembly;

			Resources.RegisterFileNamesFromAssembly(assembly);
		}

		internal static string ReadTextFromResource(string assemblyName, string path, System.Text.Encoding encoding)
		{
			assemblyName = assemblyName ?? defaultAssembly;

			Assembly assembly;
			if (!AssemblyStreamManager.assemblyNames.TryGetValue(assemblyName, out assembly))
			{
				throw new AssemblyNotFoundException(assemblyName);
			}

			string resourceName = CanonicalizeEmbeddedResourceName(assemblyName, path);
			Stream stream = assembly.GetManifestResourceStream(resourceName);
			if (stream == null)
			{
				throw new ResourceNotFoundException(assemblyName, path);
			}

			System.IO.StreamReader sr = new StreamReader(stream, encoding);

			return sr.ReadToEnd();
		}
	}
}